
#include <unistd.h>
#include <string.h>
#include <iostream>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string>

using namespace std;
#define FILE_PATH_MAX_SIZE 255

// Get the type of file.
char GetType(mode_t term)
{
	if (S_ISREG(term))
	{
		return '-';
	}
	else if (S_ISDIR(term))
	{
		return 'd';
	}
	else if (S_ISCHR(term))
	{
		return 'c';
	}
	else if (S_ISBLK(term))
	{
		return 'b';
	}
	else if (S_ISFIFO(term))
	{
		return 'p';
	}
	else if (S_ISLNK(term))
	{
		return 'l';
	}
	else if (S_ISSOCK(term))
	{
		return 's';
	}
	else
	{
		return 'U'; // Unknown type.
	}
}

// Get the permissions of file.
char *GetPerm(mode_t term)
{
	static char perm[9];
	const char *mode = "rwxrwxrwx";
	for (int i = 8; i >= 0; i--)
	{
		if (term & 1)
		{
			perm[i] = mode[i];
		}
		else
		{
			perm[i] = '-';
		}
		term = term >> 1;
	}
	return perm;
}

//Get the UserName of file.
char *GetUserName(uid_t uid)
{
	return getpwuid(uid)->pw_name;
}

//Get the GroupName of file.
char *GetGroupName(gid_t gid)
{
	return getgrgid(gid)->gr_name;
}

//Get the last modifying time of file.
string GetTime(const time_t *time)
{
	string time_str = ctime(time);
	return time_str.substr(0, time_str.length()-6);
}

//Get the properties of file.
void PrintStat(struct stat buf, char *filename)
{
	cout << GetType(buf.st_mode) << GetPerm(buf.st_mode) << "\t " << buf.st_nlink << "\t ";
	cout << GetUserName(buf.st_uid) << "\t " << GetGroupName(buf.st_gid) << "\t " << buf.st_size << "\t " << GetTime(&buf.st_mtime) << "\t " << filename << endl;
}

//Give the usage of myls.
void PrintUsage()
{
	cout << "Usage: ./myls [-perm]" << endl;
	cout << "Perimeters:" << endl;
	cout << "-l"
		 << "\t"
		 << "Show detailed information of files in the diretory." << endl;
	cout << "-L"
		 << "\t"
		 << "Dereference:  When showing file information for a symbolic link, show information for the file the link references rather than for the link itself" << endl;
	cout << "-a"
		 << "\t"
		 << "Show all hidden files." << endl;
	cout << "-A"
		 << "\t"
		 << "Show almost all hidden files, ignoring \".\" and \"..\"." << endl;
}


int MyLs(const int flag[])
{
	int (*fp)(const char *path, struct stat *buff);
	//利用flag值判断是否进行dereference, 并利用函数指针进行lstat与stat的切换调用.
	if (flag[1]) // -L
	{
		fp = stat;
	}
	else // default -l
	{
		fp = lstat;
	}
	char *path = new char[FILE_PATH_MAX_SIZE];
	if (getcwd(path, FILE_PATH_MAX_SIZE) != NULL)
	{
		DIR *currentdir = opendir(path); // Open current directory.
		if (currentdir != NULL)
		{
			struct dirent *currentdp;
			while ((currentdp = readdir(currentdir)) != NULL)
			{
				struct stat buf; // Read the information of the files.
				if (fp(currentdp->d_name, &buf) < 0)
				{
					cout << ("lstat error") << endl;
					continue;
				}
				else if (currentdp->d_name[0] == '.')
				{
					if (flag[2]) //-a
					{
						PrintStat(buf, currentdp->d_name);
					}
					else if (flag[3]) //-A
					{
						if (!(strlen(currentdp->d_name) == 1 || (strlen(currentdp->d_name) == 2 && currentdp->d_name[1] == '.')))
						{
							PrintStat(buf, currentdp->d_name);
						}
					}
				}
				else
				{
					PrintStat(buf, currentdp->d_name);
					// if(S_ISREG(buf.st_mode)){
					// 	cout<< "regular file."<<endl;
					// }
					// else{
					// 	cout<< "No regular file."<<endl;
					// }
				}
			}
			closedir(currentdir);
		}
		else
		{
			cout << "Open failed!" << endl;
		}
	}
	else
	{
		cout << "Error: getcwd fail!";
	}

	delete[] path;
	path = NULL;
}

void Ls(const int flag[])
{
	char *path = new char[FILE_PATH_MAX_SIZE];
	if (getcwd(path, FILE_PATH_MAX_SIZE) != NULL)
	{
		DIR *currentdir = opendir(path); // Open current directory.
		if (currentdir != NULL)
		{
			struct dirent *currentdp;
			while ((currentdp = readdir(currentdir)) != NULL)
			{ 
				if (currentdp->d_name[0] == '.')
				{
					if (flag[2]) //-a
					{
						cout<<currentdp->d_name<<"  ";
					}
					else if (flag[3]) //-A
					{
						if (!(strlen(currentdp->d_name) == 1 || (strlen(currentdp->d_name) == 2 && currentdp->d_name[1] == '.')))
						{
							cout<<currentdp->d_name<<"  "; 
						}
					}
				}
				else
					cout<<currentdp->d_name<<"  ";
			}
		}
		else
		{
			cout << "Open failed!" << endl;
		}
	}
	else
	{
		cout << "Error: getcwd fail!";
	}
    
	cout<<endl;
	delete[] path;
	path = NULL;
}

int main(int argc, char *argv[])
{
	int flag[4] = {0, 0, 0, 0};
	/*
	flag[0]: -l
	flag[1]: -L
	flag[2]: -a
	flag[3]: -A
	注意到, 在linux的自带ls命令中, -a的优先级高于-A
	*/
	for (int i = 1; i < argc; i++)
	{
		if (argv[i][0] != '-')
		{
			PrintUsage();
			return -1;
		}
		int len = strlen(argv[i]);
		for (int j = 1; j < len; j++)
		{
			switch (argv[i][j])
			{
			case 'l':
				flag[0] = 1;
				break;
			case 'L':
				flag[1] = 1;
				break;
			case 'a':
				flag[2] = 1;
				break;
			case 'A':
				flag[3] = 1;
				break;
			default:
				cout << "ERROR: Invalid Perimeters!" << endl;
				PrintUsage();
				return -1;
				break;
			}
		}
	}
	if (flag[0]||flag[1])
		MyLs(flag);
	else
		Ls(flag);

	return 0;
}

